import json
import random
import time

from app.base.requester import Requester
from lxml.builder import basestring

from app.libs.mongo_about.run_count import add_count
from app.libs.save_log import save_request_record, update_request_record


class RunCases:
    def __init__(self, group_s, testcases, group_id=None, space_id=None, _id=None):
        self.var_dict = {}
        self.log_info = []
        self.error = None
        self.group_id = group_id
        self.space_id = space_id
        self.testcases = testcases
        self.case_info = None
        if not group_id:
            self.group_id = testcases[0]['group_id']
        if not space_id:
            self.space_id = testcases[0]['spaceID']
        self._id = _id
        self.group_s = group_s
        self.headers_list = [
            {
                'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 10; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (iPad; CPU OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/87.0.4280.77 Mobile/15E148 Safari/604.1'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.109 Safari/537.36 CrKey/1.54.248666'
            }, {
                'User-Agent': 'Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.188 Safari/537.36 CrKey/1.54.250320'
            }, {
                'User-Agent': 'Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+'
            }, {
                'User-Agent': 'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/14.14263'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)'
            }, {
                'User-Agent': 'Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.158 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 11; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36'
            }, {
                'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1'
            }, {
                'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'
            }, {
                'User-Agent': 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1'
            }
        ]
        self.case = None
        self.headers = random.choice(self.headers_list)
        self.run_count = 0
        add_count()

    def run_case(self):
        with Requester() as s:
            t1 = time.time()

            for case in self.testcases:
                # 实时数据库case执行状态】、
                self._save_or_update_log(t1, 'RUNNING', True)
                self.case = self.group_s.before_request(case, self.var_dict)
                method, url, data, headers, params = self.get_case_data()
                request_info, response = s.execute(method=method, url=url, json=data,
                                                   headers=headers, params=params)
                self.save_case_info(request_info, response)
                if isinstance(self.case_info['response'], basestring) or isinstance(self.case_info['request'],
                                                                                    basestring):
                    self._error_ex(self.case_info['response'])
                    break

                after_info = self.group_s.after_request(self.case_info['response']['body'], case['condition'],
                                                        self.var_dict)

                if "错误类型" in after_info and isinstance(after_info, basestring):
                    self._error_ex(after_info, 'condition')

                    break

                self.log_info.append(self.case_info)

                self.var_dict = after_info
                self.run_count += 1

            self.group_s.update_status(self.group_id, time.time(), self.error)
            self._save_or_update_log(t1)

    def get_case_data(self):
        method = self.case['method']
        url = self.case['url']
        data = self.case.get('body', None)
        headers = self._check_headers(self.case.get('headers', None))
        params = self.case.get('params', None)
        if data:
            try:
                # 处理data格式
                data = eval(data)
            except:
                pass

        return method, url, data, headers, params

    def _check_headers(self, headers):
        if not headers:
            return self.headers
        headers = eval(headers)
        if not headers.get('User-Agent', None):
            headers = dict(headers, **self.headers)
        return {k: str(v) for k, v in headers.items()}

    def save_case_info(self, request_info, response):
        # TODO 做一下处理 这两个是一个对象
        self.case_info = {'request': self._request_info(request_info), 'response': self._response_info(response)}
        if not self.case_info['request']:
            self.case_info['error'] = request_info
            self.error = 'request'
            self.case_info['request']['method'] = self.case['method']
            self.case_info['request']['url'] = self.case['url']
            self.case_info['request']['headers'] = self.case['headers']
        self.case_info['request']['condition'] = self.case['condition']
        self.case_info['request']['name'] = self.case['name']

    def _request_info(self, request_info):
        if isinstance(request_info, basestring):
            return {}
        return self._get_data_info(request_info)

    def _response_info(self, response):
        if isinstance(response, basestring):
            if not self.error:
                self.error = 'response'
            return response
        try:
            body = response.json()
        except Exception:
            body = ''.join(response.text)

        return self._get_data_info(response, body)

    @staticmethod
    def _get_data_info(data, body=None):
        new_data = {
            "url": data.url,
            "headers": data.headers,

        }
        if body:
            new_data['body'] = body
            new_data['status'] = data.status_code
            return new_data
        if isinstance(data.body, bytes):
            new_data['body'] = data.body.decode('utf-8')
        else:
            new_data['body'] = data.body
        new_data['method'] = data.method
        return new_data

    def _error_ex(self, error, error_type=None):
        if not self.case_info.get('error', None):
            self.case_info['error'] = error
        if not self.error and error_type:
            self.error = error_type
        self.log_info.append(self.case_info)

    def _save_or_update_log(self, t1, run_status='OVER', debug=False):
        if self._id:
            if debug:
                if self.run_count == 0:
                    save_request_record(self.group_id, self.log_info, round(time.time() - t1, 2), self.error,
                                        self.space_id,
                                        run_status,
                                        self._id)
                else:
                    update_request_record(self.group_id, self.log_info, round(time.time() - t1, 2), self.error,
                                          self.space_id,
                                          run_status,
                                          self._id)
            else:
                update_request_record(self.group_id, self.log_info, round(time.time() - t1, 2), self.error,
                                      self.space_id,
                                      run_status,
                                      self._id)
        elif run_status == 'RUNNING':
            return
        else:
            save_request_record(self.group_id, self.log_info, round(time.time() - t1, 2), self.error, self.space_id,
                                run_status, self._id)
